﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Fadd.InversionOfControl
{
    public class AssemblyLoader
    {
        private List<Type> _loadedTypes = new List<Type>();

        public AssemblyLoader()
        {
            AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoaded;
        }

        private void OnAssemblyLoaded(object sender, AssemblyLoadEventArgs args)
        {
            ScanAssembly(args.LoadedAssembly);
        }

        private void ScanAssembly(Assembly assembly)
        {
            var rootFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            if (string.IsNullOrEmpty(assembly.Location))
                return;

            var assemblyDir = Path.GetDirectoryName(assembly.Location);
            if (!assemblyDir.StartsWith(rootFolder))
                return;

            foreach (var type in assembly.GetTypes())
            {
                if (type.IsAbstract || type.IsInterface)
                    continue;

                ScanType(type);
            }
        }

        public void LoadAllTypes()
        {
            ScanStarted(this, EventArgs.Empty);
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
                ScanAssembly(assembly);
            ScanEnded(this, EventArgs.Empty);
        }

        private void ScanType(Type type)
        {
            if (_loadedTypes.Contains(type))
                return;
            _loadedTypes.Add(type);

            var args = new TypeScannedEventArgs {ScannedType = type};
            TypeScanned(this, args);
        }

        public event EventHandler ScanStarted = delegate { };
        public event EventHandler ScanEnded = delegate { };

        public event EventHandler<TypeScannedEventArgs> TypeScanned = delegate { };
    }
}
